package com.skiving.cloud.filter;

import cn.dev33.satoken.context.SaHolder;
import cn.dev33.satoken.router.SaRouter;
import cn.dev33.satoken.same.SaSameUtil;
import cn.dev33.satoken.solon.integration.SaTokenFilter;
import cn.dev33.satoken.solon.util.SaTokenContextSolonUtil;
import cn.dev33.satoken.stp.StpUtil;
import com.skiving.cloud.common.utils.ReturnT;
import lombok.extern.slf4j.Slf4j;
import org.apache.http.HttpStatus;
import org.noear.solon.annotation.Component;
import org.noear.solon.cloud.gateway.CloudGatewayFilterSync;
import org.noear.solon.cloud.gateway.exchange.ExContext;
import org.noear.solon.core.handle.Context;
import org.noear.solon.core.handle.ContextHolder;

@Slf4j
@Component(index = 0)
public class AuthGatewayFilterImpl implements CloudGatewayFilterSync {

    /**
     * 网关鉴权
     * @param ctx 上下文
     * @return 结果
     * @throws Throwable 异常
     */
    @Override
    public boolean doFilterSync(ExContext ctx) throws Throwable {
        try {
            //1.获取 web 经典同步接口；并设为当前上下文
            Context ctx2 = ctx.toContext();
            ContextHolder.currentSet(ctx2);
            SaTokenContextSolonUtil.setContext(ContextHolder.current());
            //2.对接同步处理（使用 sa-token 接口）
            new SaTokenFilter().addInclude("/**")
                    .addExclude("/favicon.ico",
                            "/admin/auth/login",
                            "/admin/auth/captcha.jpg*",
                            "/swagger**",
                            "/healthz**",
                            "/metrics/**")
                    .setAuth(r -> SaRouter.match("/**", StpUtil::checkLogin))
                    .setError(e -> {
                        log.error(e.getMessage(), e);
                        return ReturnT.error(HttpStatus.SC_UNAUTHORIZED, e.getMessage());
                    }).setBeforeAuth(r -> {
                        // ---------- 设置一些安全响应头 ----------
                        SaHolder.getResponse()
                                // 是否可以在iframe显示视图： DENY=不可以 | SAMEORIGIN=同域下可以 | ALLOW-FROM uri=指定域名下可以
                                .setHeader("X-Frame-Options", "SAMEORIGIN")
                                // 是否启用浏览器默认XSS防护： 0=禁用 | 1=启用 | 1; mode=block 启用, 并在检查到XSS攻击时，停止渲染页面
                                .setHeader("X-Frame-Options", "1; mode=block")
                                // 禁用浏览器内容嗅探
                                .setHeader("X-Content-Type-Options", "nosniff")
                                .setHeader("Access-Control-Allow-Origin", ctx.rawHeader("Origin"))
                                .setHeader("Access-Control-Allow-Methods", "GET, POST, PUT, DELETE, OPTIONS")
                                .setHeader("Access-Control-Allow-Headers", "Accept, Content-Type, Authorization")
                                .setHeader("Access-Control-Allow-Credentials", "true");
                    }).doFilter(ctx2, ch -> {});
            //服务间调用校验使用Same-token
            ctx.rawHeaders().add(SaSameUtil.SAME_TOKEN, SaSameUtil.getToken());
            //3.检测是否要结束？
            if (ctx2.isHeadersSent()) {
                ctx2.flush();
                return false;
            } else {
                return true;
            }
        } finally {
            ContextHolder.currentRemove();
        }
    }
}
